#include "Gerador.h"

Gerador::Gerador()
{
}

Gerador::~Gerador()
{}

bool Gerador::geraCodigo(Arvore *pArvore, Hash *pHash)
{
    mTabelaHash = pHash;
    mArvore     = pArvore;
    
    geraPrograma(mArvore->getRaiz());
    
    return true;
}

void Gerador::imprime(std::ostream& saida)
{
    for (int i=0; i < rotulos.size(); i++)
    {
        saida << std::setw(6) << std::left << rotulos[i];
        saida << comandos[i] << std::endl;
    }
}

void Gerador::escreve(std::string pRotulo, std::string pCodigo)
{  
    rotulos.push_back(pRotulo);
    comandos.push_back(pCodigo);
}

void Gerador::escreve(std::string pCodigo)
{
    rotulos.push_back("");
    comandos.push_back(pCodigo);
}

void Gerador::geraPrograma(NoArvore *pNoAtual)
{
    auxLaco = 1;
    nivelAtual = 0;
    numFuncaoAtual = 0;
    
    std::list<NoArvore *>::iterator it;
    
    escreve("", "INPP");
    int variaveisGlobais = 0;
    
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {
            if (strcmp((*it)->getConteudo(), "[VARIAVEIS]") == 0)
            {
                variaveisGlobais = contaVariaveis(*it);
                if (variaveisGlobais > 0) 
                    escreve("", "AMEM "+ intToString(variaveisGlobais));
            }
            else if (strcmp((*it)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0)
            {
                escreve("DSVS PROG");
                
                if ((*it)->getFilhos() != NULL)
                    geraFuncoes(*it);
            }
            else if (strcmp((*it)->getConteudo(), "[COMANDOS]") == 0)
            {
                escreve("PROG", "NADA");
                geraComandos(*it);
            }
        }//if
    }//for
    if (variaveisGlobais > 0)
        escreve("", "DMEM "+ intToString(variaveisGlobais));
    escreve("", "PARA");
}

int Gerador::contaVariaveis(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;
    int contador = 0;
    
    if (pNoAtual->temFilho() == false) return 0;
    
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {
            contador++;
        }//if
    }//for
    
    return contador;
}

std::string Gerador::intToString(int valor)
{
    std::stringstream ss;
    std::string str;
    ss << valor;
    ss >> str;
    
    return str;
}

bool Gerador::geraComandos(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;
    
    if (pNoAtual->temFilho() == false) return true;
    
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {           
            if (strcmp((*it)->getConteudo(), "read") == 0)
            {
                geraComandoRead(*it);
            }
            else if (strcmp((*it)->getConteudo(), "write") == 0)
            {
                geraComandoWrite(*it);
            }
            else if (strcmp((*it)->getConteudo(), "while") == 0)
            {
                geraComandoWhile(*it);
            }
            else if (strcmp((*it)->getConteudo(), ":=") == 0)
            {
                geraComandoAtribuicao(*it);
            }
            else if (strcmp((*it)->getConteudo(), "if") == 0)
            {
                geraComandoIf(*it);
            }
            else if (strcmp((*it)->getConteudo(), "[CALL PROCEDURE|CALL FUNCTION]") == 0)
            {
                geraChamadaProcedimento(*it);
            }
            
        }//if
    }//for
}

bool Gerador::geraComandoIf(NoArvore *pNoAtual)
{
    int lacoLocal = auxLaco;
    auxLaco++;
    
    std::list<NoArvore *>::iterator it;
    
    //aqui estamos em conditions
    it = pNoAtual->getFilhos()->begin();
       
    //gera a condição
    geraExpressao( *((*it)->getFilhos()->begin()) );
    
    escreve("", "DSVF L"+intToString(lacoLocal));
    
    it++;
    
    //agora estamos em [then]
    geraComandos(*it);
    
    it++;
    
    //se sim, ainda tem o else
    if (it != pNoAtual->getFilhos()->end())
    {        
        if (strcmp((*it)->getConteudo(), "[else]") == 0)
        {
            escreve("", "DSVS F"+intToString(lacoLocal));
            escreve("L"+intToString(lacoLocal), "NADA");
            
            geraComandos(*it);
            
            escreve("F"+intToString(lacoLocal), "NADA");
        }
    }
    else
    {
        escreve("L"+intToString(lacoLocal), "NADA");
    }
}

bool Gerador::geraComandoRead(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;
        
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {
            std::string procedimentoPai = buscarProcedimento((*it)->getConteudo(), pNoAtual);

            int nivel = (mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai))->getNivel();

            IdentificadorVariavel *idVar = (IdentificadorVariavel *) this->mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai);
            int deslocamento = idVar->getDeslocamento();
            
            escreve("", "LEIT");
            escreve("", "ARMZ "+ intToString(nivel) + "," + intToString(deslocamento));
        }
    }
}

std::string Gerador::buscarProcedimento(std::string pIdentificador, NoArvore *pNoPai)
{
    NoArvore *percorre = pNoPai->getNoPai();
    std::list<NoArvore *>::iterator it;

    while(percorre != NULL)
    {
        if (percorre->getConteudo() != NULL)
        {
            if (strcmp(percorre->getConteudo(), "[FUNCAO]")       == 0 ||
                strcmp(percorre->getConteudo(), "[PROCEDIMENTO]") == 0  )
            {
                if (percorre->temFilho() == true)
                {
                    it = percorre->getFilhos()->begin();

                    if (this->mTabelaHash->getHash(pIdentificador, (*it)->getConteudo()) != NULL)
                        return (*it)->getConteudo();
                }
            }
            else if (strcmp(percorre->getConteudo(), "program") == 0)
            {
                if (this->mTabelaHash->getHash(pIdentificador, "program") != NULL)
                    return "program";
            }
        }
        percorre = percorre->getNoPai();
    }
    return "";
}

bool Gerador::geraComandoWrite(NoArvore *pNoAtual)
{   
    std::list<NoArvore *>::iterator it;
      
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {           
            std::string procedimentoPai = buscarProcedimento((*it)->getConteudo(), pNoAtual);

            int nivel = (mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai))->getNivel();

            IdentificadorVariavel *idVar = (IdentificadorVariavel *) this->mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai);
            int deslocamento = idVar->getDeslocamento();

            escreve("", "CRVL "+ intToString(nivel) + "," + intToString(deslocamento));
            escreve("", "IMPR");
        }
        else if ((*it)->getExpressao() != NULL)
        {
            geraExpressao(*it);
            
            escreve("", "IMPR");
        }
    }
}

bool Gerador::geraComandoWhile(NoArvore *pNoAtual)
{
    int lacoLocal = auxLaco;
    auxLaco++;
    
    std::list<NoArvore *>::iterator it;
    
    it = pNoAtual->getFilhos()->begin();
    
    escreve("L"+intToString(lacoLocal), "NADA");
    
    //manda o nó filho que tem a lista de expressao
    geraExpressao( *((*it)->getFilhos()->begin()) );
    
    escreve("", "DSVF F"+intToString(lacoLocal));
    
    it++;
    
    //agora estamos em [do]
    geraComandos(*it);
    
    escreve("", "DSVS L"+intToString(lacoLocal));
    escreve("F"+intToString(lacoLocal), "NADA");
}

bool Gerador::geraComandoAtribuicao(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;
    std::string tipo;
    int deslocamento, nivel;
    
    //primeiro guarda o identificador que recebe a atribuicao
    it = pNoAtual->getFilhos()->begin();
    
    std::string identRecebeAtt = (*it)->getConteudo();
    
    //avança para a expressão
    it++; 
    geraExpressao(*it);
    
    std::string procedimentoPai = buscarProcedimento(identRecebeAtt, pNoAtual);
    tipo = typeid(*(mTabelaHash->getHash(identRecebeAtt, procedimentoPai))).name();
          
    if (tipo == "21IdentificadorProcFunc")
    {
        IdentificadorProcFunc *id = (IdentificadorProcFunc *) mTabelaHash->getHash(identRecebeAtt, procedimentoPai);
        deslocamento = id->getDeslocamento();
    }
    else
    {
        IdentificadorVariavel *idv = (IdentificadorVariavel *) mTabelaHash->getHash(identRecebeAtt, procedimentoPai);
        deslocamento = idv->getDeslocamento();
    }
    
    escreve("", "ARMZ "+ intToString(nivelAtual) + "," + intToString(deslocamento));
}

bool Gerador::geraExpressao(NoArvore *pNoAtual)
{   
    bool neg = false;
    std::list<std::string>::iterator expressao, it;
    
    std::list<std::string> lado1;
    std::list<std::string> lado2;
    std::string comp, finalLado2;

    int lado = 1;
    
    //primeiro de tudo separa os dois lados da comparação (se houver uma)
    for (expressao = pNoAtual->getExpressao()->begin(); expressao != pNoAtual->getExpressao()->end(); expressao++)
    {
        if (*expressao == "<" || *expressao == "<=" || *expressao == ">" || *expressao == ">=" || *expressao == "=")
        {
            lado = 2;
            comp = *expressao;
        }
        else
        {
            if (lado == 1) lado1.push_back(*expressao);
            else lado2.push_back(*expressao);
        }
    }
    
    if (lado1.size() == pNoAtual->getExpressao()->size()) geraExpressaoNormal(pNoAtual->getExpressao(), pNoAtual);
    else
    {
        if (comp == "<") comp = "CMME";
        else if (comp == "<=") comp = "CMEG";
        else if (comp == ">") comp = "CMMA";
        else if (comp == ">=") comp = "CMAG";
        else if (comp == "=") comp = "CMIG";
        
        for (it = lado2.begin(); it != lado2.end(); it++) finalLado2 = *it;
        
        if ( *(lado1.begin()) == "(" && finalLado2 == ")" ) 
        {
            lado1.pop_front();
            lado2.pop_back();
        }
        
        if ( *(lado1.begin()) == "not" && finalLado2 == ")" ) 
        {
            lado1.pop_front();
            lado1.pop_front();
            lado2.pop_back();
            
            neg = true;
        }
        
        //teve comparação
        geraExpressaoNormal(&lado1, pNoAtual); //gera o primeiro lado da comparação
        geraExpressaoNormal(&lado2, pNoAtual); //gera o segundo lado da comparação
        escreve("", comp);
        
        if (neg)
            escreve("NEGA");
    }
    
}

bool Gerador::geraChamadaProcedimento(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;
    std::list<std::string>::iterator its;
    
    it = pNoAtual->getFilhos()->begin();
    std::list<std::string> *expressao = (*it)->getExpressao();
    
    its = expressao->begin();
    
    std::string nomeProc = *its;
        
    std::string procedimentoPai = buscarProcedimento(nomeProc, pNoAtual);
    IdentificadorProcFunc *id = (IdentificadorProcFunc *) mTabelaHash->getHash(nomeProc, procedimentoPai);
    
    if ( id->getCategoria() == 3 )
    {
        expressao->pop_front();
        escreve("AMEM 1"); //se é função tem que alocar o retorno, senão zica tudo
    }
    
    geraExpressaoNormal(expressao, pNoAtual);
        
    escreve("CHPR "+ id->nomeProcMepa +"1");
}

bool Gerador::geraExpressaoNormal(std::list<std::string> *pExpressao, NoArvore *pNoAtual)
{
    std::list<std::string>::iterator expressao, it;
    std::string final;
    
    for (it = pExpressao->begin(); it != pExpressao->end(); it++) final = *it;
        
    if ( *(pExpressao->begin()) == "(" && final == ")" ) 
    {
        pExpressao->pop_front();
        pExpressao->pop_back();
    }
    
    NoAux* arvore;
    
    //primeiramente geramos a árvore da expressão
    arvore = geraArvoreExpressao(pExpressao, pNoAtual);
       
    //coloca na árvore na forma posfixa
    arvore = geraArvorePosFixa(arvore);
       
    //agora vamos gerar o código
    geraCodigoExpressao(arvore, pNoAtual);
}

bool Gerador::geraCodigoExpressao(NoAux* raiz, NoArvore *pNoAtual)
{
    std::list<NoAux*>::iterator it, it2;
    std::string ident;
    
    for (it = raiz->filhos.begin(); it != raiz->filhos.end(); it++)
    {
        if ((*it)->isFuncao)
        {
            //aloca espaço para retorno
            escreve("AMEM 1");
            
            //passa por todos os parâmetros da função gerando o código deles
            for (it2 = (*it)->filhos.begin(); it2 != (*it)->filhos.end(); it2++)
            {
                geraCodigoExpressao(*it2, pNoAtual);
            }
            
            std::string procedimentoPai = buscarProcedimento((*it)->conteudo, pNoAtual);
            IdentificadorProcFunc *id = (IdentificadorProcFunc *) mTabelaHash->getHash((*it)->conteudo, procedimentoPai);
            
            //chama a função
            escreve("CHPR "+id->nomeProcMepa+"1");
        }
        else
        {
            ident = (*it)->conteudo;
            
            //agora gera o código de verdade
            if (ident == "true")
                escreve("CRCT 1");
            else if (ident == "false")
                escreve("CRCT 0");
            else if (isNumero(ident))
                escreve("CRCT "+ident);
            else if (ident == "+")
                escreve("SOMA");
            else if (ident == "*")
                escreve("MULT");
            else if (ident == "div")
                escreve("DIVI");
            else if (ident == "-")
                escreve("SUBT");
            else
            {
                //é identificador
                std::string procedimentoPai = buscarProcedimento(ident, pNoAtual);

                int nivel = (mTabelaHash->getHash(ident, procedimentoPai))->getNivel();

                IdentificadorVariavel *idVar = (IdentificadorVariavel *) this->mTabelaHash->getHash(ident, procedimentoPai);
                int deslocamento = idVar->getDeslocamento();

                escreve("CRVL "+ intToString(nivel) + "," + intToString(deslocamento));
            }
        }
    }
}

NoAux* Gerador::geraArvoreExpressao(std::list<std::string> *pExpressao, NoArvore *pNoAtual)
{
    std::list<std::string>::iterator it, prox, iaux;
    NoAux* raiz = new NoAux;
    NoAux* no, *no2;
    std::list<std::string> *filaAux;
    int ignora = 0;
    bool sair = false;
    
    raiz->conteudo = "r";
    raiz->isFuncao = false;
    
    for (it = pExpressao->begin(); it != pExpressao->end(); it++)
    {       
        //cria um novo nó com o conteúdo do item
        no = new NoAux;
        no->conteudo = *it;
        no->isFuncao = false;
               
        //verifica se o próximo é um (
        prox = it;
        prox++;
        
        if (isIdentificadorFuncao(*it, pNoAtual))
        {
            if (prox == pExpressao->end())
                if (isIdentificadorFuncao(*it, pNoAtual)) no->isFuncao = true;
            else if (*prox != "(")
                if (isIdentificadorFuncao(*it, pNoAtual)) no->isFuncao = true;
        }
        
        if (prox != pExpressao->end())
        {
            if (*prox == "(")
            {
                //é um (, agora vamos ver se isto é uma função
                if (isIdentificadorFuncao(*it, pNoAtual))
                {
                    no->isFuncao = true;
                    
                    prox++; //agora it aponta para primeiro item
                    
                    filaAux = new std::list<std::string>;
                    
                    sair = false;
                    while (!sair)
                    {                       
                        filaAux->push_back(*prox);
                        prox++;
                        
                        if (*prox == "(") ignora++;
                        
                        if (ignora == 0)
                        {
                            if (*prox == "," || *prox == ")") //cria o no parametro
                            {
                                no2 = geraArvoreExpressao(filaAux, pNoAtual);
                                no2->conteudo = "p";
                                no->filhos.push_back(no2);

                                filaAux->clear();
                            }
                            if (*prox == ",") prox++;
                            if (*prox == ")") sair = true;
                        }
                        else 
                            if (*prox == ")") ignora--;
                    }
                    
                    it = prox;
                    
                }//é funcao
                
            }//prox é (
        }//tem prox
        
        //coloca nesse no na árvore
        raiz->filhos.push_back(no);
    }
    
    return raiz;
}

bool Gerador::isIdentificadorFuncao(std::string ident, NoArvore *pNoAtual)
{
    if (isNumero(ident))
        return false;
    if (isBooleano(ident))
        return false;
    if (ident == "*" || ident == "-" || ident == "div" || ident == "+")
        return false;
    
    std::string tipo;
    
    std::string procedimentoPai = buscarProcedimento(ident, pNoAtual);
    tipo = typeid(*(mTabelaHash->getHash(ident, procedimentoPai))).name();
    
    if (tipo == "21IdentificadorProcFunc")
        return true;
    else
        return false;
}

NoAux* Gerador::geraArvorePosFixa(NoAux* raiz)
{
    //gera pos fixa dessa expressao
    raiz->filhos = infixaToPosfixa(raiz->filhos);
    
    //agora vai passar por todas as funções rodando essa função em suas expressoes
    std::list<NoAux*>::iterator it, it2;
                   
    for (it = raiz->filhos.begin(); it != raiz->filhos.end(); it++)
    {   
        if ((*it)->isFuncao)
        {            
            for (it2 = (*it)->filhos.begin(); it2 != (*it)->filhos.end(); it2++)
            {
                (*it2)->filhos = infixaToPosfixa((*it2)->filhos);
                
                geraArvorePosFixa(*it2);
            }
        }
    }
    
    return raiz;
}

std::list<NoAux*> Gerador::infixaToPosfixa(std::list<NoAux*> pExpressao)
{
    std::list<NoAux*>::iterator it;

    NoAux* posfixa[256];

    NoAux* pilha[256]; int t;
    int n, i, j;
    
    NoAux* noaux;

    noaux = new NoAux;
    noaux->conteudo = "(";
    noaux->isFuncao = false;
    pExpressao.push_front(noaux);
    
    noaux = new NoAux;
    noaux->conteudo = ")";
    noaux->isFuncao = false;
    pExpressao.push_back(noaux);
    
    n = pExpressao.size();
    t = 0;
    j = 0;
    it = pExpressao.begin();
    pilha[t++] = (*it);
    
    NoAux* x = new NoAux;
    
    for (it = pExpressao.begin(); it != pExpressao.end(); it++)
    {
        if ((*it)->conteudo == "(")
        {
            pilha[t++] = (*it);
        }
        else if ((*it)->conteudo == ")")
        {
            while (1)
            {
                x = pilha[--t];
                if (x->conteudo == "(") break;
                posfixa[j++] = x;
            }
        }
        else if ((*it)->conteudo == "+" || (*it)->conteudo == "-")
        {
            while (1)
            {
                x = pilha[t-1];
                if (x->conteudo == "(") break;
                --t;
                posfixa[j++] = x;
            }
            pilha[t++] = (*it);
        }
        else if ((*it)->conteudo == "*" || (*it)->conteudo == "div")
        {
            while (1)
            {
                x = pilha[t-1];
                if (x->conteudo == "(" || x->conteudo == "+" || x->conteudo == "-") 
                    break;
                --t;
                posfixa[j++] = x;
            }
            pilha[t++] = (*it);
         }
         else
         {
              posfixa[j++] = (*it);
         }
    }
    
    noaux = new NoAux;
    noaux->conteudo = "kend";
    noaux->isFuncao = false;
    posfixa[j] = noaux;
    
    std::list<NoAux*> retorno;
    
    i = 0;
    while (posfixa[i]->conteudo != "kend")
    {
        retorno.push_back(posfixa[i]);
        i++;
    }
       
    return retorno;
}

bool Gerador::isNumero(std::string pIdentificador)
{
    for (int i = 0; i < pIdentificador.length(); i++)
        if (pIdentificador[i] < '0' || pIdentificador[i] > '9')
            return false;
    return true;
}

bool Gerador::isBooleano(std::string pIdentificador)
{
    if (pIdentificador == "true" || pIdentificador == "false")
        return true;

    return false;
}

bool Gerador::geraFuncoes(NoArvore *pNoAtual)
{
    nivelAtual++;
    
    std::list<NoArvore *>::iterator it, itf;
    char tipo;
    int numParamentros, numVariaveis;
    std::string nomefuncao;
    
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {           
            //verificando se este nó é de função ou procedimento
            //isto é útil no final, que o retorno difere nos dois
            if (strcmp((*it)->getConteudo(), "[FUNCAO]") == 0)
                tipo = 'f';
            else if (strcmp((*it)->getConteudo(), "[PROCEDIMENTO]") == 0)
                tipo = 'p';
            
            //pegando o nome da função
            itf = (*it)->getFilhos()->begin();
            nomefuncao = "P"+intToString(numFuncaoAtual)+"_";
            numFuncaoAtual++;
            
            (*itf)->nomeProcMepa = nomefuncao;
            std::string procedimentoPai = buscarProcedimento((*itf)->getConteudo(), pNoAtual);
            IdentificadorProcFunc *id = (IdentificadorProcFunc *) mTabelaHash->getHash((*itf)->getConteudo(), procedimentoPai);
            id->nomeProcMepa = nomefuncao;
            
            //contando parâmetros
            itf++;
            if (strcmp((*itf)->getConteudo(), "[PARAMETROS]") == 0)
            {
                numParamentros = contaVariaveis(*itf);
                itf++;
            }
            else numParamentros = 0;
            
            //contando variáveis
            numVariaveis = contaVariaveis(*itf);
            
            //gerando cabeçalho da função
            escreve(nomefuncao+ "1", "NADA");
            escreve("ENPR "+ intToString(nivelAtual));
            if (numVariaveis > 0) 
                escreve("AMEM "+ intToString(numVariaveis));
            escreve("DSVS "+ nomefuncao + "2");
                       
            //procedimentos/funções aninhados
            itf++;
            if ((*itf)->getFilhos() != NULL)
                geraFuncoes(*itf);
            
            //código corpo da função
            escreve(nomefuncao+ "2", "NADA");
            
            //código do programa
            itf++;
            geraComandos(*itf);
            
            //desaloca memória e retorna
            if (numVariaveis > 0) 
                escreve("DMEM "+ intToString(numVariaveis));
            
            if (tipo == 'f') //funcao tem retorno
                escreve("RTPR "+ intToString(nivelAtual) +","+ intToString(numParamentros) );
            else //procedimento nem tem retorno
                escreve("RTPR "+ intToString(nivelAtual) );
        }
    }//for it
    
    nivelAtual--;
}
